﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;

namespace BitcoinArbitrage.Data
{
    public class ArbitrageData
    {
        DataSet _dsRaw;
        DataSet _tgtDs;
        DateTime _timeLogin;

        OrderController _controller;
        
        public ArbitrageData()
        {
            _controller = new OrderController();

            initialized();
        }

        private void initialized()
        {
            InitializedDataSet();

            InitializedDataSetTGT();

           
        }

        private void InitializedDataSet()
        {
            DataTable _dt;

            _dsRaw = new DataSet("ARBITRRAGE");

            //KORBIT 데이터
            _dt = new DataTable(StringConstant.TABLE_KORBIT_ASK);
            _dsRaw.Tables.Add(_dt);

            _dt = new DataTable(StringConstant.TABLE_KORBIT_BID);
            _dsRaw.Tables.Add(_dt);

            _dt = new DataTable(StringConstant.TABLE_KORBIT_OPEN);
            _dsRaw.Tables.Add(_dt);

            //BTCe 데이터
            _dt = new DataTable(StringConstant.TABLE_BTCE_ASK);
            _dsRaw.Tables.Add(_dt);

            _dt = new DataTable(StringConstant.TABLE_BTCE_BID);
            _dsRaw.Tables.Add(_dt);

            _dt = new DataTable(StringConstant.TABLE_BTCE_OPEN);
            _dsRaw.Tables.Add(_dt);  
        }

        private void InitializedDataSetTGT()
        {
            DataTable dt;
            DataColumn col;

            _tgtDs = new DataSet("TARGET");

            //누적 평균매수/매도가 - 수량 테이블 생성
            dt = new DataTable(StringConstant.TABLE_ACC_KORBIT_ASK);
            col = dt.Columns.Add();
            col.ColumnName = "BTCPRICE";
            col.DataType = System.Type.GetType("System.Decimal");
            col = dt.Columns.Add();
            col.ColumnName = "BTCPRICE_AVG";
            col.DataType = System.Type.GetType("System.Decimal");
            col = dt.Columns.Add();
            col.ColumnName = "BTCAMOUNT";
            col.DataType = System.Type.GetType("System.Decimal");
            _tgtDs.Tables.Add(dt);

            dt = _tgtDs.Tables[StringConstant.TABLE_ACC_KORBIT_ASK].Clone();
            dt.TableName = StringConstant.TABLE_ACC_KORBIT_BID;
            _tgtDs.Tables.Add(dt);

            dt = _tgtDs.Tables[StringConstant.TABLE_ACC_KORBIT_ASK].Clone();
            dt.TableName = StringConstant.TABLE_ACC_BTCE_ASK;
            _tgtDs.Tables.Add(dt);

            dt = _tgtDs.Tables[StringConstant.TABLE_ACC_KORBIT_ASK].Clone();
            dt.TableName = StringConstant.TABLE_ACC_BTCE_BID;
            _tgtDs.Tables.Add(dt);  
            
            //매수/매도 차익시 이익 계산
            dt = new DataTable(StringConstant.TABLE_MARGINBUY);
            col = dt.Columns.Add();
            col.ColumnName = "EST_INCOME";
            col.DataType = System.Type.GetType("System.Decimal");
            col = dt.Columns.Add();
            col.ColumnName = "EST_PER_INCOME";
            col.DataType = System.Type.GetType("System.Decimal");
            col = dt.Columns.Add();
            col.ColumnName = "BTCAMOUNT";
            col.DataType = System.Type.GetType("System.Decimal");
            col = dt.Columns.Add();
            col.ColumnName = "BUY_BTCPRICE";
            col.DataType = System.Type.GetType("System.Decimal");
            col = dt.Columns.Add();
            col.ColumnName = "SELL_BTCPRICE";
            col.DataType = System.Type.GetType("System.Decimal");
            _tgtDs.Tables.Add(dt);

            dt = _tgtDs.Tables[StringConstant.TABLE_MARGINBUY].Clone();
            dt.TableName = StringConstant.TABLE_MARGINSELL;
            _tgtDs.Tables.Add(dt);            

        }

        /// <summary>
        /// Logins this instance.
        /// </summary>
        public void Login()
        {
            _controller.Login();

            if (IsLogin())
            {
                ResetRereshTokenTime();
            }            
        }

        private void ResetRereshTokenTime()
        {
            _timeLogin = DateTime.Now;
        }

        /// <summary>
        /// Determines whether this instance is login.
        /// </summary>
        /// <returns></returns>
        public bool IsLogin()
        {
            return _controller.IsLogin();
        }

        public DateTime RefreshTokenTime()
        {
            return _timeLogin;
        }

        public void RefreshToken()
        {
            if ((DateTime.Now - RefreshTokenTime()).TotalMinutes < 30)
                return;

            _controller.RefreshToken();

            ResetRereshTokenTime();
        }

        public void RefreshData()
        {
            _controller.RefreshData(ref _dsRaw);

            CalculateAccTables();

            CalculateMaginTables();
        }

        public void CheckMaginalTrading()
        {
          
            double dbMarginalBuy;
            double dbMarginalBuyKRWBTCPrice;
            double dbMarginalSellUSDBTCPrice;
            double dbMarginalSell;
            double dbMarginalSellKRWBTCPrice;
            double dbMarginalBuyUSDBTCPrice;

            DataTable dt;


            dt = _tgtDs.Tables[StringConstant.TABLE_MARGINBUY];
            DataRow[] Rows = dt.Select("BTCAMOUNT > 1.0", "BTCAMOUNT ASC");

            if (Rows.Length == 0)
            {
                dbMarginalBuy = 0.0;
                dbMarginalBuyKRWBTCPrice = 0.0;
                dbMarginalSellUSDBTCPrice = 0.0;
            }
            else
            {
                dbMarginalBuy = Double.Parse(Rows[0]["EST_PER_INCOME"].ToString());
                dbMarginalBuyKRWBTCPrice = Double.Parse(Rows[0]["BUY_BTCPRICE"].ToString());
                dbMarginalSellUSDBTCPrice = Double.Parse(Rows[0]["SELL_BTCPRICE"].ToString());
            }

            dt = _tgtDs.Tables[StringConstant.TABLE_MARGINSELL];
            Rows = dt.Select("BTCAMOUNT > 1.0", "BTCAMOUNT ASC");

            if (Rows.Length == 0)
            {
                dbMarginalSell = 0.0;
                dbMarginalSellKRWBTCPrice = 0.0;
                dbMarginalBuyUSDBTCPrice = 0.0;
            }
            else
            {
                dbMarginalSell = Double.Parse(Rows[0]["EST_PER_INCOME"].ToString());
                dbMarginalSellKRWBTCPrice = Double.Parse(Rows[0]["SELL_BTCPRICE"].ToString());
                dbMarginalBuyUSDBTCPrice = Double.Parse(Rows[0]["BUY_BTCPRICE"].ToString());
            }

            if (dbMarginalBuy > 2000.0)
            {
                Console.WriteLine(dbMarginalBuy.ToString());
            }
            else if (dbMarginalSell > 3000.0)
            {
                Console.WriteLine(dbMarginalSell.ToString());
            }

        }

        public void CanccelOrders()
        {
        }


        private void CalculateAccTables()
        {
            CalculateAccTable(StringConstant.TABLE_KORBIT_ASK, StringConstant.TABLE_ACC_KORBIT_ASK, eCalcAccTable.ASK);
            CalculateAccTable(StringConstant.TABLE_KORBIT_BID, StringConstant.TABLE_ACC_KORBIT_BID, eCalcAccTable.BID);
            CalculateAccTable(StringConstant.TABLE_BTCE_ASK, StringConstant.TABLE_ACC_BTCE_ASK, eCalcAccTable.ASK);
            CalculateAccTable(StringConstant.TABLE_BTCE_BID, StringConstant.TABLE_ACC_BTCE_BID, eCalcAccTable.BID);
        }

        private void CalculateAccTable(string strSrcTable, string strTgtTable, eCalcAccTable eCalc)
        {
            double dbPrice;
            double dbAmount;
            double dbAveragePrice;
            double dbAccAmount;
            double dbTotalAmount;
            double dbCurrency;
            DataRow[] dRows;
                
            dbCurrency = Double.Parse(_controller.CurrencyUSDKRW());

            DataTable srcTable = _dsRaw.Tables[strSrcTable];
            DataTable tgtTable = _tgtDs.Tables[strTgtTable];

            if (eCalc == eCalcAccTable.ASK)
            {
                dRows = srcTable.Select("", "BitcoinPrice ASC");
            }
            else
            {
                dRows = srcTable.Select("", "BitcoinPrice DESC");
            }
            

            dbAveragePrice = 0.0;
            dbTotalAmount = 0.0;
            dbAccAmount = 0.0;

            tgtTable.Rows.Clear();
            foreach (DataRow dr in dRows)
            {
                dbPrice = Double.Parse(dr["BitcoinPrice"].ToString());

                if (eCalc == eCalcAccTable.ASK)
                {
                    dbAmount = Double.Parse(dr["AskVolume"].ToString());
                }
                else
                {
                    dbAmount = Double.Parse(dr["BidVolume"].ToString());
                }
                

                dbAccAmount = dbAccAmount + dbAmount;
                dbTotalAmount = dbTotalAmount + dbPrice * dbAmount;
                dbAveragePrice = dbTotalAmount / dbAccAmount;
                
                //새로운 행을 가져온다.
                DataRow row = tgtTable.NewRow();
                row["BTCPRICE"] = dbPrice;
                row["BTCPRICE_AVG"] = dbAveragePrice;
                row["BTCAMOUNT"] = dbAccAmount;
                tgtTable.Rows.Add(row);
            }
        }

        private void CalculateMaginTables()
        {
            CalculateMaginTable(StringConstant.TABLE_ACC_KORBIT_ASK, StringConstant.TABLE_ACC_BTCE_BID, StringConstant.TABLE_MARGINBUY, eCalcMarginTable.BUY);
            CalculateMaginTable(StringConstant.TABLE_ACC_BTCE_ASK, StringConstant.TABLE_ACC_KORBIT_BID, StringConstant.TABLE_MARGINSELL, eCalcMarginTable.SELL);
        }

        private void CalculateMaginTable(string strBuyTable, string strSellTable, string strTargetTable, eCalcMarginTable eCalc)
        {

            double dbKRWCurrency;
            double dbKRWBTC;
            double dbUSDCurrency;
            double dbUSDBTC;
            double dbUSDKRW;

            double dbEstIncome;
            double dbEstPerIncome;
            double dbBuyBTCPrice;
            double dbBuyBTCAmount;
            double dbBuyAvgBTCPrice;
            double dbSellBTCPrice;
            double dbSellBTCAmount;
            double dbSellAvgBTCPrice;

            double dbAvailableCurrencyBTC;
            double dbAvailableBTCAmount;
            double dbTradeBTC;
            double dbDiscountRate;

            //데이터 설정
            DataTable buyTable = _tgtDs.Tables[strBuyTable];
            DataTable sellTable = _tgtDs.Tables[strSellTable];
            DataTable tgtTable = _tgtDs.Tables[strTargetTable];

            DataRow[] buyRows = buyTable.Select("", "BTCPRICE ASC");
            DataRow[] sellRows = sellTable.Select("", "BTCPRICE DESC"); 
            

            //지갑 가져오기
            dbDiscountRate = 0.002;
            dbUSDKRW = Currency();
            dbKRWCurrency = BalanceKRWCurrency();
            dbKRWBTC = BalanceKRWBTC();
            dbUSDCurrency = BalanceUSDCurrency();
            dbUSDBTC = BalanceUSDBTC();

            tgtTable.Clear();

            if (buyTable.Rows.Count < 50 || sellTable.Rows.Count < 50)
                return;

            for (int iBuyIndex = 0; iBuyIndex < 50; iBuyIndex++)
            {
                for (int iSellIndex = 0; iSellIndex < 50; iSellIndex++)
                {
                    dbBuyBTCPrice = Double.Parse(buyRows[iBuyIndex]["BTCPRICE"].ToString());
                    dbBuyBTCAmount = Double.Parse(buyRows[iBuyIndex]["BTCAMOUNT"].ToString());
                    dbBuyAvgBTCPrice = Double.Parse(buyRows[iBuyIndex]["BTCPRICE_AVG"].ToString());
                    dbSellBTCPrice = Double.Parse(sellRows[iSellIndex]["BTCPRICE"].ToString());
                    dbSellBTCAmount = Double.Parse(sellRows[iSellIndex]["BTCAMOUNT"].ToString());
                    dbSellAvgBTCPrice = Double.Parse(sellRows[iSellIndex]["BTCPRICE_AVG"].ToString());

                    if (eCalc == eCalcMarginTable.BUY)
                    {
                        dbAvailableCurrencyBTC = Math.Floor(dbKRWCurrency / dbBuyBTCPrice);
                        dbEstPerIncome = dbSellAvgBTCPrice * dbUSDKRW * (1.0 - dbDiscountRate) - dbBuyAvgBTCPrice;
                    }
                    else
                    {
                        dbAvailableCurrencyBTC = Math.Floor(dbUSDCurrency / dbBuyBTCPrice);
                        dbEstPerIncome = dbSellAvgBTCPrice - dbBuyAvgBTCPrice * dbUSDKRW * (1.0 + dbDiscountRate);
                    }

                    if (dbBuyBTCAmount > dbSellBTCAmount)
                    {
                        dbAvailableBTCAmount = dbSellBTCAmount;
                    }
                    else
                    {
                        dbAvailableBTCAmount = dbBuyBTCAmount;
                    }

                    if (dbAvailableCurrencyBTC < dbAvailableBTCAmount)
                    {
                        dbTradeBTC = dbAvailableCurrencyBTC;

                    }
                    else
                    {
                        dbTradeBTC = dbAvailableBTCAmount;
                    }

                    dbEstIncome = dbEstPerIncome * dbTradeBTC;


                    DataRow dr = tgtTable.NewRow();
                    dr["EST_INCOME"] = dbEstIncome;
                    dr["EST_PER_INCOME"] = dbEstPerIncome;
                    dr["BTCAMOUNT"] = dbTradeBTC;
                    dr["BUY_BTCPRICE"] = dbBuyBTCPrice;
                    dr["SELL_BTCPRICE"] = dbSellBTCPrice;
                    tgtTable.Rows.Add(dr);

                }
            }
        }

        private void CalculateMaginalTrading()
        {
            //누적 평균 가격 테이블 계산
            CalculateAccTables();

            CalculateMaginTables();

            CheckMaginalTrading();
        }

        public double Currency()
        {
            if (_controller.CurrencyUSDKRW() == null)
                return 0.0;

            return Double.Parse(_controller.CurrencyUSDKRW());
        }

        public double BalanceKRWCurrency()
        {
            if (_controller.BalanceKRW() == null)
                return 0.0;

            return Double.Parse(_controller.BalanceKRW());
        }

        public double BalanceKRWBTC()
        {
            if (_controller.BalanceKRWBTC() == null)
                return 0.0;

            return Double.Parse(_controller.BalanceKRWBTC());
        }

        public double BalanceUSDCurrency()
        {
            if (_controller.BalanceUSD() == null)
                return 0.0;

            return Double.Parse(_controller.BalanceUSD());
        }

        public double BalanceUSDBTC()
        {
            if (_controller.BalanceUSDBTC() == null)
                return 0.0;

            return Double.Parse(_controller.BalanceUSDBTC());
        }

        public DataTable PublicExchangeKorbitAsk()
        {
            return _tgtDs.Tables[StringConstant.TABLE_ACC_KORBIT_ASK];
        }

        public DataTable PublicExchangeKorbitBid()
        {
            return _tgtDs.Tables[StringConstant.TABLE_ACC_KORBIT_BID];
        }

        public DataTable PublicExchangeBTCeAsk()
        {
            return _tgtDs.Tables[StringConstant.TABLE_ACC_BTCE_ASK];
        }

        public DataTable PublicExchangeBTCeBid()
        {
            return _tgtDs.Tables[StringConstant.TABLE_ACC_BTCE_BID];
        }

        public double MaginalBuyProfit()
        {
            return MarginalProfit(eCalcMarginTable.BUY);
        }

        public double MarginalSellProfit()
        {
            return MarginalProfit(eCalcMarginTable.SELL);
        }

        private double MarginalProfit(eCalcMarginTable eCalc)
        {
            double dbReturn = 0.0;

            DataRow row = MarginalProfitRow(eCalc);

            if (row != null)
            {
                dbReturn =  Double.Parse(row["EST_PER_INCOME"].ToString());
            }
            
            return dbReturn;
        }

        public MarginalProfit MarginalProfitData(eCalcMarginTable eCalc)
        {
            MarginalProfit objReturn = new MarginalProfit();

            DataRow row = MarginalProfitRow(eCalc);

            if (row != null)
            {
                objReturn._eCalc = eCalc;
                objReturn._dbEstProfit = Double.Parse(row["EST_PER_INCOME"].ToString());
                objReturn._dbBTCAmount = Double.Parse(row["BTCAMOUNT"].ToString());

                if (eCalc == eCalcMarginTable.BUY)
                {
                    objReturn._dbKRWPrice = Double.Parse(row["BUY_BTCPRICE"].ToString());
                    objReturn._dbUSDPrice = Double.Parse(row["SELL_BTCPRICE"].ToString());
                }
                else
                {
                    objReturn._dbUSDPrice = Double.Parse(row["BUY_BTCPRICE"].ToString());
                    objReturn._dbKRWPrice = Double.Parse(row["SELL_BTCPRICE"].ToString());
                }
            }
            
            return objReturn;
        }

        public DataRow MarginalProfitRow(eCalcMarginTable eCalc)
        {
            DataRow dtRow = null;

            DataTable dt;

            if (eCalc == eCalcMarginTable.BUY)
            {
                dt = _tgtDs.Tables[StringConstant.TABLE_MARGINBUY];
            }
            else
            {
                dt = _tgtDs.Tables[StringConstant.TABLE_MARGINSELL];
            }

            DataRow[] Rows = dt.Select("BTCAMOUNT >= 1.0", "EST_PER_INCOME DESC,BTCAMOUNT ASC ");

            if (Rows.Length > 0)
            {
                dtRow = Rows[0];
            }

            return dtRow;
        }

        public void TradingMarginal()
        {
            if (!IsLogin())
                return;

            MarginalProfit margin = MarginalProfitData(eCalcMarginTable.BUY);

            if (margin._dbEstProfit > 1500.0)
            {
                if (margin._dbKRWPrice == 0.0 || margin._dbUSDPrice == 0.0)
                    return;

                TradeMarginalBuy("1.0", margin._dbKRWPrice.ToString(), margin._dbUSDPrice.ToString());
            }


            margin = MarginalProfitData(eCalcMarginTable.SELL);

            if (margin._dbEstProfit > 2500.0)
            {
                if (margin._dbKRWPrice == 0.0 || margin._dbUSDPrice == 0.0)
                    return;

                TradeMarginalSell("1.0", margin._dbKRWPrice.ToString(), margin._dbUSDPrice.ToString());
            }

        }

        public void TradeMarginalBuy(string strBTCAmount, string strKRWBTCPrice, string strUSDBTCPrice)
        {
            double dbBTCAmount = Double.Parse(strBTCAmount); 
            double dbKRWBTCPrice = Double.Parse(strKRWBTCPrice);
            double dbUSDBTCPrice = Double.Parse(strUSDBTCPrice);

            if (!IsTradable(eCalcMarginTable.BUY, dbBTCAmount, dbKRWBTCPrice, dbUSDBTCPrice))
                return;

            _controller.OrderMaginalBuy(dbKRWBTCPrice.ToString(), dbBTCAmount.ToString(), dbUSDBTCPrice.ToString(), (dbBTCAmount * 1.002).ToString());
        }

        public void TradeMarginalSell(string strBTCAmount, string strKRWBTCPrice, string strUSDBTCPrice)
        {
            double dbBTCAmount = Double.Parse(strBTCAmount); 
            double dbKRWBTCPrice = Double.Parse(strKRWBTCPrice);
            double dbUSDBTCPrice = Double.Parse(strUSDBTCPrice);

            if (!IsTradable(eCalcMarginTable.SELL, dbBTCAmount, dbKRWBTCPrice, dbUSDBTCPrice))
                return;

            _controller.OrderMaginalSell(dbKRWBTCPrice.ToString(), dbBTCAmount.ToString(), dbUSDBTCPrice.ToString(), (dbBTCAmount * 1.002).ToString());
        }

        public bool IsTradable(eCalcMarginTable eCalc, double dbBTCAmount, double dbKRWBTCPrice, double dbUSDBTCPrice)
        {
            bool bReturn = false;

            double dbKRWCurrency = BalanceKRWCurrency();
            double dbKRWBTC = BalanceKRWBTC();
            double dbUSDCurrency = BalanceUSDCurrency();
            double dbUSDBTC = BalanceUSDBTC();

            double dbSellBTCAmount;
            double dbBuyBTCAmount;

            if (eCalc == eCalcMarginTable.BUY)
            {
                dbBuyBTCAmount = dbKRWCurrency / dbKRWBTCPrice;
                dbSellBTCAmount = dbUSDBTC * 0.998;
            }
            else
            {
                dbBuyBTCAmount = dbUSDCurrency / dbUSDBTCPrice;
                dbSellBTCAmount = dbKRWBTC;
            }

            if (dbBuyBTCAmount > dbBTCAmount && dbSellBTCAmount > dbBTCAmount)
                bReturn = true;
            
            return bReturn;
        }

 
    }
}
